package com.santoker.user.service.component;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;

/**
 * Mybatis + Redis 二级缓存
 *
 * @author jiangs
 * @since 2021/10/13
 */
public final class MybatisRedisCache implements Cache {

    private static final Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private String id;

    private RedisTemplate redisTemplate;

    public MybatisRedisCache() {}

    public MybatisRedisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("key is empty");
        }
        log.info("====== 缓存ID：{}", id);
        this.id = id;
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public int getSize() {
        try {
            this.getRedisTemplate();
            Long size = redisTemplate.opsForHash().size(this.id);
            log.info("====== 获取缓存Size， id：{}，size：{}", id, size);
            return size.intValue();
        } catch (Exception e) {
            log.error("====== 获取缓存Size异常", e);
        }
        return 0;
    }

    @Override
    public void putObject(final Object key, final Object value) {
        try {
            this.getRedisTemplate();
            Boolean hasKey = redisTemplate.hasKey(this.id);
            log.info("====== 加入缓存: id：{}，key：{}，value：{}", id, this.getCacheKey(key), null);
            redisTemplate.opsForHash().put(this.id, this.getCacheKey(key), value);

            if (!hasKey) {
                // 设置缓存过期时间为30分钟，只有KEY不存在时才去设置过期时间，否则只要有新的缓存加入就会重置。
                redisTemplate.expire(this.id, 30, TimeUnit.MINUTES);
            }
        } catch (Exception e) {
            log.error("====== 加入缓存异常", e);
        }
    }

    @Override
    public Object getObject(final Object key) {
        try {
            this.getRedisTemplate();
            Object hashVal = redisTemplate.opsForHash().get(this.id, this.getCacheKey(key));
//            log.info("====== 读取缓存: id：{}，key：{}，value：{}", id, this.getCacheKey(key), null);
            return hashVal;
        } catch (Exception e) {
            log.error("====== 读取缓存异常", e);
        }
        return null;
    }

    @Override
    public Object removeObject(final Object key) {
        try {
            this.getRedisTemplate();
            log.info("====== 删除缓存: id：{}，key：{}", id, this.getCacheKey(key));
            redisTemplate.opsForHash().delete(this.id, this.getCacheKey(key));
        } catch (Exception e) {
            log.error("====== 删除缓存异常", e);
        }
        return null;
    }

    @Override
    public void clear() {
        try {
            this.getRedisTemplate();
            log.info("====== 清除缓存: id：{}", id);
            redisTemplate.delete(this.id);
        } catch (Exception e) {
            log.error("====== 清除缓存异常", e);
        }
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.readWriteLock;
    }

    private String getCacheKey(Object key) {
        return Pattern.compile("\t|\r|\n| ").matcher(String.valueOf(key)).replaceAll("");
    }

    private RedisTemplate getRedisTemplate() {
        if (this.redisTemplate == null) {
            this.redisTemplate = ApplicationContextHolder.getRedisTemplate();
        }
        return redisTemplate;
    }

}
